from typing import Optional
from datetime import datetime, timedelta
from collections import namedtuple

from django.db.models import QuerySet, Q
from django.utils import timezone as dj_timezone

from apps.app_users.models import Email
from apps.app_tx_meeting.models import ReservationNoShow, MeetingReservation, NoShowWhitelist
from apps.app_tx_meeting.helpers import UTCAdd8TimeHelper


_NoShowRestrict = namedtuple('NoShowRestrict', ['start', 'end', 'week_noshows'])
class NoShowRestrict(_NoShowRestrict):
    """
    start: 违约限制起始时间；datetime or None
    end: 违约限制结束时间；datetime or None
    week_noshows: 违约周的违约记录；List
    """
    pass


class ReservationNoShowManager:
    @staticmethod
    def create_reservation_noshow(
            reservation: MeetingReservation,
            username: str, user_nickname: str,
            creation_time: datetime = None,
            valid_status: str = ReservationNoShow.ValidStatus.VALID.value,
            remarks: str = '',
            email_notice: Optional[Email] = None
    ) -> ReservationNoShow:
        if not creation_time:
            creation_time = dj_timezone.now()

        obj = ReservationNoShow(
            reservation=reservation,
            username=username,
            user_nickname=user_nickname,
            creation_time=creation_time,
            valid_status=valid_status,
            remarks=remarks,
            email_notice=email_notice
        )
        obj.save(force_insert=True)
        return obj

    @staticmethod
    def filter_noshow_queryset(
            time_gte: datetime, time_lt: datetime,
            username: Optional[str] = None,
            valid_status: list = None,
    ) -> QuerySet:
        """
        :param time_gte: >= 此时间戳的 违约
        :param time_lt: < 此时间戳的 违约
        :param username: 违约用户名
        :param valid_status: 有效状态
        """
        lookups = {}

        if time_gte:
            lookups['creation_time__gte'] = time_gte

        if time_lt:
            lookups['creation_time__lt'] = time_lt

        if username:
            lookups['username'] = username

        if valid_status:
            if isinstance(valid_status, str):
                lookups['valid_status'] = valid_status
            else:
                lookups['valid_status__in'] = valid_status

        return ReservationNoShow.objects.filter(**lookups)

    @staticmethod
    def get_noshow_restrict_time(username: str) -> NoShowRestrict:
        """
        通过最近2周的违约记录，获取违约惩罚限制时间
        """
        nt = UTCAdd8TimeHelper.now()
        week_start, next_week_start = UTCAdd8TimeHelper.get_week_range(nt)
        pre_week_start = week_start - timedelta(days=7)

        qs = ReservationNoShowManager.filter_noshow_queryset(
            username=username, valid_status=ReservationNoShow.ValidStatus.VALID.value,
            time_gte=pre_week_start, time_lt=next_week_start
        )
        current_week_noshows = []
        pre_week_noshows = []
        for obj in qs:
            # 上周违约记录
            if pre_week_start <= obj.creation_time < week_start:
                pre_week_noshows.append(obj)
            # 本周违约记录
            elif week_start <= obj.creation_time < next_week_start:
                current_week_noshows.append(obj)

        # 先看本周违约次数是否达到惩罚条件; 按时间正序，第2次违约时间开始7天内不能约定会议
        if len(current_week_noshows) >= 2:
            current_week_noshows.sort(key=lambda item: item.creation_time, reverse=False)
            restrict_start = current_week_noshows[1].creation_time
            restrict_end = restrict_start + timedelta(days=7)
            restrict_noshows = current_week_noshows
        elif len(pre_week_noshows) >= 2:
            pre_week_noshows.sort(key=lambda item: item.creation_time, reverse=False)
            restrict_start = pre_week_noshows[1].creation_time
            restrict_end = restrict_start + timedelta(days=7)
            restrict_noshows = pre_week_noshows
        else:
            restrict_start = None
            restrict_end = None
            restrict_noshows = []

        # 转换北京时间
        if restrict_start:
            restrict_start = UTCAdd8TimeHelper.astimezone(restrict_start)

        if restrict_end:
            restrict_end = UTCAdd8TimeHelper.astimezone(restrict_end)

        return NoShowRestrict(start=restrict_start, end=restrict_end, week_noshows=restrict_noshows)


class NoShowWhitelistManager:
    @staticmethod
    def get_whitelist(wl_id: str) -> Optional[NoShowWhitelist]:
        return NoShowWhitelist.objects.filter(pk=wl_id).first()

    @staticmethod
    def get_whitelist_by_name(username: str) -> Optional[NoShowWhitelist]:
        return NoShowWhitelist.objects.filter(username=username).first()

    @staticmethod
    def is_user_in_whitelist(username: str) -> bool:
        return NoShowWhitelist.objects.filter(username=username).exists()

    @staticmethod
    def create_whitelist(username: str, user_nickname: str, remarks: str, creator: str) -> NoShowWhitelist:
        wl = NoShowWhitelist(
            username=username,
            user_nickname=user_nickname if user_nickname else '',
            remarks=remarks if remarks else '',
            creator=creator if creator else ''
        )
        wl.save(force_insert=True)
        return wl

    @staticmethod
    def filter_whitelist_queryset(username: Optional[str] = None, search: Optional[str] = None) -> QuerySet:
        qs = NoShowWhitelist.objects.all()
        if username:
            qs = qs.filter(username=username)

        if search:
            qs = qs.filter(
                Q(username__icontains=search) | Q(user_nickname__icontains=search) | Q(remarks__icontains=search))

        return qs.order_by('-creation_time')
